﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using Elentok.Common.Wpf.ViewModels.Dialogs;

namespace Elentok.Common.Wpf
{
  /// <summary>
  /// Interaction logic for Dialog.xaml
  /// </summary>
  public partial class Dialog : Window
  {
    /// <summary>
    /// Initializes a new instance of the <see cref="Dialog"/> class
    /// </summary>
    /// <param name="viewModel"></param>
    public Dialog(IDialogViewModel viewModel) : this(viewModel, null) { }

    /// <summary>
    /// Initializes a new instance of the <see cref="Dialog"/> class
    /// </summary>
    /// <param name="viewModel"></param>
    /// <param name="resourceDictionaryUri">Points to the resource dictionary 
    /// that contains the datatemplate for the view model</param>
    public Dialog(IDialogViewModel viewModel, Uri resourceDictionaryUri)
    {
      InitializeComponent();
      this.DataContext = viewModel;
      viewModel.Closed += ViewModel_Closed;

      if (resourceDictionaryUri == null)
      {
        // try the default URI
        string name = viewModel.GetType().Name.Replace("ViewModel", string.Empty);
        resourceDictionaryUri =
          new Uri("/Elentok.Common.Wpf;component/Themes/Dialogs/" + name + ".Generic.xaml", UriKind.Relative);
      }

      var dictionary = Application.LoadComponent(resourceDictionaryUri) as ResourceDictionary;
      if (dictionary == null)
        throw new ResourceNotFoundException(resourceDictionaryUri);

      this.Resources.MergedDictionaries.Add(dictionary);
    }


    private bool userPressedCloseButton = false;
    
    private void ViewModel_Closed(object sender, DialogClosedEventArgs e)
    {
      if (!this.DialogResult.HasValue)
        this.DialogResult = e.Result;

      // if the user pressed the close button, then we're already within
      // the OnClosing method and we don't need to call "Close" again
      if (userPressedCloseButton)
        return;

      Close();
    }

    /// <summary>
    /// Gets the view model associated with this dialog
    /// </summary>
    public IDialogViewModel ViewModel
    {
      get { return (IDialogViewModel)this.DataContext; }
    }

    /// <summary>
    /// This method is called when the "Close" method is called
    /// or when the user clicks the window's "Close" button.
    /// </summary>
    /// <param name="e"></param>
    protected override void OnClosing(CancelEventArgs e)
    {
      // if the view model isn't closed, it means the closing
      // was triggered by the window's close button, so we
      // need to execute the view model's Cancel command
      if (!this.ViewModel.IsClosed)
      {
        this.userPressedCloseButton = true;

        if (this.ViewModel.CancelCommand != null)
          this.ViewModel.CancelCommand.Execute(null);
      }

      // if the view model is still not closed, abort the close
      if (!this.ViewModel.IsClosed)
      {
        this.userPressedCloseButton = false;
        e.Cancel = true;
      }

      base.OnClosing(e);
    }

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Window.ContentRendered"/> event.
    /// </summary>
    /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
    protected override void OnContentRendered(EventArgs e)
    {
      base.OnContentRendered(e);

      this.ViewModel.OnShown();
    }
  }

  /// <summary>
  /// This exception is thrown when a <see cref="Uri"/> cannot be found.
  /// </summary>
  [Serializable]
  public class ResourceNotFoundException : Exception
  {
    /// <summary>
    /// Initializes a new instance of the <see cref="ResourceNotFoundException"/> class.
    /// </summary>
    /// <param name="uri">The URI.</param>
    public ResourceNotFoundException(Uri uri)
      : this(string.Format(CultureInfo.CurrentCulture,
        "Resource with Uri '{0}' was not found", uri.ToString())) { }
    /// <summary>
    /// Initializes a new instance of the <see cref="ResourceNotFoundException"/> class.
    /// </summary>
    public ResourceNotFoundException() { }
    /// <summary>
    /// Initializes a new instance of the <see cref="ResourceNotFoundException"/> class.
    /// </summary>
    /// <param name="message">The message.</param>
    public ResourceNotFoundException(string message) : base(message) { }
    /// <summary>
    /// Initializes a new instance of the <see cref="ResourceNotFoundException"/> class.
    /// </summary>
    /// <param name="message">The message.</param>
    /// <param name="inner">The inner exception.</param>
    public ResourceNotFoundException(string message, Exception inner) : base(message, inner) { }
    /// <summary>
    /// Initializes a new instance of the <see cref="ResourceNotFoundException"/> class.
    /// </summary>
    /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
    /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
    /// <exception cref="T:System.ArgumentNullException">
    /// The <paramref name="info"/> parameter is null.
    /// </exception>
    /// <exception cref="T:System.Runtime.Serialization.SerializationException">
    /// The class name is null or <see cref="P:System.Exception.HResult"/> is zero (0).
    /// </exception>
    protected ResourceNotFoundException(
    System.Runtime.Serialization.SerializationInfo info,
    System.Runtime.Serialization.StreamingContext context)
      : base(info, context) { }
  }
}
